﻿@page "/account/register"
@using CleanArchitecture.Blazor.Application.Common.Interfaces.MultiTenant
@using CleanArchitecture.Blazor.Application.Features.Tenants.DTOs
@using CleanArchitecture.Blazor.Domain.Identity
@using CleanArchitecture.Blazor.Application.Common.Constants.Localization
@using CleanArchitecture.Blazor.Application.Common.Constants.Roles
@using Microsoft.AspNetCore.WebUtilities
@using System.Text
@using CleanArchitecture.Blazor.Application.Features.Identity.Notifications.UserActivation
@using System.ComponentModel.DataAnnotations
@using System.Globalization
@using CleanArchitecture.Blazor.Server.UI.Pages.Identity.Login
@using CleanArchitecture.Blazor.Server.UI.Components.Autocompletes

@inherits OwningComponentBase

@inject IStringLocalizer<Register> L
@inject ILogger<Register> Logger

<PageTitle>@Title</PageTitle>

<div class="d-flex flex-column gap-y-3">
    <div class="d-flex flex-column">
        <EditForm Model="Input"  OnValidSubmit="RegisterUser" FormName="register">
            <DataAnnotationsValidator />
            <MudText Typo="Typo.h4" GutterBottom="true">@L["Sign Up"]</MudText>
            <MudText>
                @L["Have an account?"] <MudLink Href="@Login.PageUrl">@L["Sign In"]</MudLink>
            </MudText>
          
            <MultiTenantAutocomplete @bind-Value="Input.Tenant"
                        T="TenantDto"
                        Label="@L["Tenant"]"
                        Variant="Variant.Outlined"
                        Required="true"
                        Class="my-4"
                        For="@(() => Input.Tenant)" />
            <MudTextField @bind-Value="Input.UserName" 
                          Label="@L["User Name"]"
                          Variant="Variant.Outlined"
                          Required="true"
                          Placeholder="user name"
                          Class="my-4"
                          For="@(() => Input.UserName)" />


            <MudTextField @bind-Value="Input.Email" 
                          Label="@L["E-mail"]"
                          Variant="Variant.Outlined"
                          Required="true"
                          Placeholder="email"
                          Class="my-4"
                          For="@(() => Input.Email)" />

            <MudTextField @bind-Value="Input.Password" 
                          Label="@L["Password"]"
                          Variant="Variant.Outlined"
                          Required="true"
                          InputType="@(isPasswordVisible ? InputType.Text : InputType.Password)"
                          Placeholder="password"
                          Class="my-4"
                          For="@(() => Input.Password)"
                          Adornment="Adornment.End"
                          AdornmentIcon="@(isPasswordVisible ? Icons.Material.Filled.Visibility : Icons.Material.Filled.VisibilityOff)"
                          OnAdornmentClick="@(() => isPasswordVisible = !isPasswordVisible)" />
            <MudTextField @bind-Value="Input.ConfirmPassword" 
                          Label="@L["Confirm Password"]"
                          Variant="Variant.Outlined"
                          Required="true"
                          InputType="@(isConfirmPasswordVisible ? InputType.Text : InputType.Password)"
                          Placeholder="Confirm password"
                          Class="my-4"
                          For="@(() => Input.ConfirmPassword)"
                          Adornment="Adornment.End"
                          AdornmentIcon="@(isConfirmPasswordVisible ? Icons.Material.Filled.Visibility : Icons.Material.Filled.VisibilityOff)"
                          OnAdornmentClick="@(() => isConfirmPasswordVisible = !isConfirmPasswordVisible)" />
            <TimeZoneAutocomplete @bind-Value="Input.TimeZoneId" 
                     T="string"
                     Label="@L["Time Zone"]"
                     Variant="Variant.Outlined"
                     Required="true"
                     Class="my-4"
                     For="@(() => Input.TimeZoneId)" />
            <LanguageAutocomplete @bind-Value="Input.LanguageCode"
                    T="string"
                    Label="@L["Language"]"
                    Variant="Variant.Outlined"
                    Required="true"
                    Class="my-4"
                    For="@(() => Input.LanguageCode)" />
            <div Class="d-flex justify-space-between align-center mb-1">
                <MudCheckBox @bind-Value="Input.AgreeToTerms" 
                             Label="@L["I agree to the terms and privacy"]"
                             Required="true"
                             For="@(() => Input.AgreeToTerms)" />
                <div class="mud-input-helper-text mud-input-error">
                    <div class="d-flex">
                        <ValidationMessage For="() => Input.AgreeToTerms" class="mud-input-error" />
                    </div>
                </div>
            </div>

            <MudButton Variant="Variant.Filled"
                       Color="Color.Primary"
                       Size="Size.Large"
                       ButtonType="ButtonType.Submit"
                       FullWidth="true">
                <MudText>@L["Register"]</MudText>
            </MudButton>
        </EditForm>
    </div>
</div>

@code {

    public const string PageUrl = "/account/register";
    private string Title = "Sign Up";

    [SupplyParameterFromQuery] private string? ReturnUrl { get; set; }
    private string? message;
    private bool isPasswordVisible = false;
    private bool isConfirmPasswordVisible = false;

    private InputModel Input { get; set; } = new();
    private UserManager<ApplicationUser> _userManager = null!;
    private RoleManager<ApplicationRole> _roleManager = null!;
    private SignInManager<ApplicationUser> _signInManager = null!;


    protected override  Task OnInitializedAsync()
    {
        Title = L["Sign Up"];

        _userManager = ScopedServices.GetRequiredService<UserManager<ApplicationUser>>();
        _roleManager = ScopedServices.GetRequiredService<RoleManager<ApplicationRole>>();
        _signInManager = ScopedServices.GetRequiredService<SignInManager<ApplicationUser>>();

        return Task.CompletedTask;

    }

    public async Task RegisterUser(EditContext editContext)
    {
        var user = new ApplicationUser();
        user.TenantId = Input.TenantId;
        user.LanguageCode = Input.LanguageCode;
        user.TimeZoneId = Input.TimeZoneId;
        user.Email = Input.Email;
        user.UserName = Input.UserName;
        user.Created = DateTime.UtcNow;
        var role = await _roleManager.Roles.Where(x=>x.TenantId== Input.TenantId && x.Name==RoleName.Basic).FirstOrDefaultAsync();
        if (role is null){
            role = new ApplicationRole
            {
                Name = RoleName.Basic,
                NormalizedName = RoleName.Basic.ToUpperInvariant(),
                TenantId = Input.TenantId
            };
            var roleResult = await _roleManager.CreateAsync(role);
            if (!roleResult.Succeeded)
            {
                message = string.Join(", ", roleResult.Errors.Select(error => error.Description));
                Snackbar.Add(message, Severity.Error);
                return;
            }
        }
        var result = await _userManager.CreateAsync(user, Input.Password);
        if (!result.Succeeded)
        {
            message = string.Join(", ", result.Errors.Select(error => error.Description));
            Snackbar.Add(message, Severity.Error);
            return;
        }

        result = await _userManager.AddToRoleAsync(user, RoleName.Basic);
        if (!result.Succeeded)
        {
            message = string.Join(", ", result.Errors.Select(error => error.Description));
            Snackbar.Add(message, Severity.Error);
            return;
        }

        var userId = await _userManager.GetUserIdAsync(user);
        Logger.LogInformation("New user account created. User ID: {UserId}, Tenant ID: {TenantId}.",
            Input.UserName,
            userId,
            Input.TenantId,
            RoleName.Basic);
        if (_userManager.Options.SignIn.RequireConfirmedEmail)
        {
            var code = await _userManager.GenerateEmailConfirmationTokenAsync(user);
            code = WebEncoders.Base64UrlEncode(Encoding.UTF8.GetBytes(code));
            var callbackUrl = Navigation.GetUriWithQueryParameters(
                Navigation.ToAbsoluteUri(ConfirmEmail.PageUrl).AbsoluteUri,
                new Dictionary<string, object?> { ["userId"] = userId, ["code"] = code, ["returnUrl"] = "/" });
            await Mediator.Publish(new UserActivationNotification(callbackUrl, Input.Email, userId, Input.UserName));
            var url = Navigation.GetUriWithQueryParameters(
                Navigation.ToAbsoluteUri(RegisterConfirmation.PageUrl).AbsoluteUri,
                new Dictionary<string, object?> { ["email"] = Input.Email, ["EmailConfirmationLink"] = callbackUrl });
            Navigation.NavigateTo(url);
        }
        else if (_userManager.Options.SignIn.RequireConfirmedAccount)
        {
            var code = await _userManager.GenerateEmailConfirmationTokenAsync(user);
            code = WebEncoders.Base64UrlEncode(Encoding.UTF8.GetBytes(code));
            var callbackUrl = Navigation.GetUriWithQueryParameters(
                Navigation.ToAbsoluteUri(ConfirmEmail.PageUrl).AbsoluteUri,
                new Dictionary<string, object?> { ["userId"] = userId, ["code"] = code });
            var url = Navigation.GetUriWithQueryParameters(
                Navigation.ToAbsoluteUri(RegisterConfirmation.PageUrl).AbsoluteUri,
                new Dictionary<string, object?> { ["email"] = Input.Email, ["EmailConfirmationLink"] = callbackUrl });
            Navigation.NavigateTo(url);
        }
        else
        {

            Navigation.NavigateTo(Login.PageUrl);
        }
    }

    

   

    public sealed class InputModel
    {
        [Required]
        [StringLength(100, ErrorMessage = "The {0} must be at least {2} and at max {1} characters long.", MinimumLength = 2)]
        public string UserName { get; set; } = "";

        [Required]
        [EmailAddress]
        [Display(Name = "Email")]
        public string Email { get; set; } = "";

        [Required]
        [StringLength(100, ErrorMessage = "The {0} must be at least {2} and at max {1} characters long.", MinimumLength = 6)]
        [DataType(DataType.Password)]
        [Display(Name = "Password")]
        public string Password { get; set; } = "";

        [DataType(DataType.Password)]
        [Display(Name = "Confirm password")]
        [Compare("Password", ErrorMessage = "The password and confirmation password do not match.")]
        public string? ConfirmPassword { get; set; } = "";

        [Range(typeof(bool), "true", "true", ErrorMessage = "You must agree to the terms.")]
        public bool AgreeToTerms { get; set; } = true;

        [Required]
        [Display(Name = "Tenant Id")]
        public string? TenantId => Tenant?.Id;
        [Display(Name = "Time Zone")]
        public string? TimeZoneId { get; set; } = TimeZoneInfo.Local.Id;
        [Display(Name = "Language")]
        public string? LanguageCode { get; set; } = CultureInfo.CurrentCulture.Name;
        [Display(Name = "Tenant")]
        [Required]
        public TenantDto? Tenant { get; set; } = null;
    }

}